Skip to content

cli add back init options#1379

Merged
BilalG1 merged 4 commits intodevfrom
emulator-setup-fixes
Apr 27, 2026
Merged

cli add back init options#1379
BilalG1 merged 4 commits intodevfrom
emulator-setup-fixes

Conversation

@BilalG1
Copy link
Copy Markdown
Collaborator

@BilalG1 BilalG1 commented Apr 23, 2026

Summary by CodeRabbit

  • New Features

    • Added a "create-cloud" mode to the CLI init flow.
    • New interactive project creation flow that can prompt for display name and select/create a team-backed project.
  • Behavior Changes

    • Init now resolves mode from flags, config, or interactive prompts; prompts to choose linking vs creating when inputs are missing.
    • Non-interactive runs now error when required inputs are absent; cloud linking offers auto-create in interactive mode.
  • Refactor

    • Centralized auth, project-creation, and env key writing for clearer, safer linking and creation flows.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment Apr 25, 2026 0:17am
stack-backend Ready Ready Preview, Comment Apr 25, 2026 0:17am
stack-dashboard Ready Ready Preview, Comment Apr 25, 2026 0:17am
stack-demo Ready Ready Preview, Comment Apr 25, 2026 0:17am
stack-docs Ready Ready Preview, Comment Apr 25, 2026 0:17am
stack-preview-backend Ready Ready Preview, Comment Apr 25, 2026 0:17am
stack-preview-dashboard Ready Ready Preview, Comment Apr 25, 2026 0:17am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

Adds a "create-cloud" init mode, resolves init mode from flags or prompts, centralizes auth/session and env-key writing, enables interactive cloud project creation when none exist, prevents unsafe local config overwrites in non-interactive mode, and moves interactive project-creation into a shared helper.

Changes

Cohort / File(s) Summary
CLI init & linking
packages/stack-cli/src/commands/init.ts
Introduce "create-cloud" mode; add validateOptions; resolve effective mode from flags or prompts; change handleLink to accept resolved mode; add interactive link-source selection and safer create/link flows.
Auth/session & env helpers
packages/stack-cli/src/commands/init.ts (helpers extracted/added)
Add ensureLoggedInSession to centralize login/session establishment and writeProjectKeysToEnv to create internal API keys and write .env with stricter validation and errors.
Local config safety
packages/stack-cli/src/commands/init.ts
Refuse overwriting existing stack.config.ts in non-interactive mode; prompt for overwrite in interactive mode.
Project command refactor
packages/stack-cli/src/commands/project.ts
Remove in-file interactive prompts and non-interactive checks; delegate interactive flow and team handling to createProjectInteractively, passing optional --display-name.
Interactive project helper
packages/stack-cli/src/lib/create-project.ts
Add exported createProjectInteractively and CreateProjectOptions to handle interactive/non-interactive project creation, team retrieval/validation, and call user.createProject.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as Client (CLI)
  participant Auth as Auth Service (Auth)
  participant API as Stack API (API)
  participant FS as Filesystem (.env)

  rect rgba(135,206,250,0.5)
    CLI->>Auth: ensureLoggedInSession() (prompt/login if needed)
    Auth-->>CLI: session/token
  end

  rect rgba(144,238,144,0.5)
    CLI->>API: listOwnedProjects(session)
    API-->>CLI: projects[]
    alt no owned projects (interactive)
      CLI->>CLI: prompt create vs cancel
      CLI->>API: createProjectInteractively(session, prompts)
      API-->>CLI: project (with internal keys)
    else has projects or link chosen
      CLI->>API: select/link existing project
      API-->>CLI: project (with keys)
    end
  end

  rect rgba(255,182,193,0.5)
    CLI->>FS: writeProjectKeysToEnv(project.keys)
    FS-->>CLI: .env written
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • nams1570

Poem

🐰 I hop and prompt beneath the cloud,

I log you in and sing aloud.
"Create or link?" I softly ask,
I write the keys and do the task.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description contains only the CONTRIBUTING.md reminder boilerplate with no actual descriptive content about the changes, objectives, or rationale. Add a meaningful description explaining the key changes, such as restoring the interactive link option, adding flag validation, handling file overwrite prompts, and improving error messages.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'cli add back init options' is vague and does not clearly convey the specific changes made. It uses generic terminology ('add back') that doesn't explain what was restored or why. Use a more specific title that describes the main change, such as 'cli: restore interactive link option and add init flag validation' or similar.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch emulator-setup-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 23, 2026

Greptile Summary

This PR restores two previously commented-out interactive select prompts in the CLI init command: one to choose between linking an existing project or creating a new one, and a second to choose between linking from a config file or from the cloud dashboard.

  • The interactive flow now produces a \"link\" mode value that is absent from the InitOptions.mode union type (\"create\" | \"link-config\" | \"link-cloud\"). A user who passes --mode link on the CLI would set hasFlags = true, bypass the non-interactive terminal guard, and then hit an interactive select inside handleLink — silently breaking non-interactive usage.

Confidence Score: 3/5

Mostly safe to merge for interactive use, but the "link" mode value mismatch can break non-interactive invocations silently.

One P1 finding: the "link" value returned by the interactive select is not in the InitOptions.mode type, so --mode link passes the non-interactive guard but then hangs waiting for a second prompt. The rest of the change is a straightforward restoration of commented-out UX code.

packages/stack-cli/src/commands/init.ts — specifically the InitOptions type definition and the hasFlags guard logic.

Important Files Changed

Filename Overview
packages/stack-cli/src/commands/init.ts Re-enables two previously commented-out interactive select prompts for mode and source selection; introduces a type mismatch where the interactive flow produces a "link" mode value not present in the InitOptions.mode union type.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[stack init] --> B{opts.mode set?}
    B -- yes --> C[hasFlags = true\nskip non-interactive check]
    B -- no --> D{isNonInteractiveEnv?}
    D -- yes --> E[throw CliError]
    D -- no --> F["select: link | create"]
    C --> G[mode = opts.mode]
    F --> G
    G --> H{mode === link\nor link-config\nor link-cloud?}
    H -- yes --> I[handleLink]
    H -- mode === create --> J[handleCreate]
    H -- else --> K[throw CliError: Unknown mode]
    I --> L{opts.mode === link-config?}
    L -- yes --> M[source = config-file]
    L -- no --> N{opts.mode === link-cloud?}
    N -- yes --> O[source = cloud]
    N -- no --> P["select: config-file | cloud\n⚠️ requires interactive terminal"]
    M --> Q[handleLinkFromConfigFile]
    O --> R[handleLinkFromCloud]
    P --> R
    P --> Q
Loading

Comments Outside Diff (1)

  1. packages/stack-cli/src/commands/init.ts, line 17 (link)

    P1 --mode link not a valid CLI flag but produced by interactive prompt

    The interactive select returns "link" as the mode value (line 63), but InitOptions.mode is typed as "create" | "link-config" | "link-cloud""link" is absent. Commander.js does not validate the flag value against TypeScript types, so a user who passes --mode link on the CLI will have hasFlags = true (bypassing the non-interactive terminal guard) yet will hit a second interactive select inside handleLink because neither opts.mode === "link-config" nor opts.mode === "link-cloud" match. This silently breaks non-interactive usage with --mode link. Consider adding "link" to InitOptions.mode and handling it explicitly in handleLink, or documenting that "link" is not a valid CLI value.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/stack-cli/src/commands/init.ts
    Line: 17
    
    Comment:
    **`--mode link` not a valid CLI flag but produced by interactive prompt**
    
    The interactive `select` returns `"link"` as the mode value (line 63), but `InitOptions.mode` is typed as `"create" | "link-config" | "link-cloud"``"link"` is absent. Commander.js does not validate the flag value against TypeScript types, so a user who passes `--mode link` on the CLI will have `hasFlags = true` (bypassing the non-interactive terminal guard) yet will hit a second interactive `select` inside `handleLink` because neither `opts.mode === "link-config"` nor `opts.mode === "link-cloud"` match. This silently breaks non-interactive usage with `--mode link`. Consider adding `"link"` to `InitOptions.mode` and handling it explicitly in `handleLink`, or documenting that `"link"` is not a valid CLI value.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/stack-cli/src/commands/init.ts
Line: 17

Comment:
**`--mode link` not a valid CLI flag but produced by interactive prompt**

The interactive `select` returns `"link"` as the mode value (line 63), but `InitOptions.mode` is typed as `"create" | "link-config" | "link-cloud"``"link"` is absent. Commander.js does not validate the flag value against TypeScript types, so a user who passes `--mode link` on the CLI will have `hasFlags = true` (bypassing the non-interactive terminal guard) yet will hit a second interactive `select` inside `handleLink` because neither `opts.mode === "link-config"` nor `opts.mode === "link-cloud"` match. This silently breaks non-interactive usage with `--mode link`. Consider adding `"link"` to `InitOptions.mode` and handling it explicitly in `handleLink`, or documenting that `"link"` is not a valid CLI value.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "cli init changes" | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/stack-cli/src/commands/init.ts (1)

60-66: Consider dropping the explicit : string annotation to preserve type narrowing.

Annotating mode as string widens away the useful union. With inference, mode would be typed as "create" | "link-config" | "link-cloud" | "link" (from opts.mode ∪ the prompt's value literals), which lets TypeScript exhaustively verify the if/else if/else chain at lines 70–78 and catches typos at compile time. With : string, the throw new CliError(Unknown mode: ${mode}) on line 77 is the only guard against mistakes.

♻️ Proposed refactor
-  const mode: string = opts.mode ?? await select({
+  const mode = opts.mode ?? await select({
     message: "Would you like to link to an existing project, or create a new one?",
     choices: [
       { name: "Link an existing project", value: "link" as const },
       { name: "Create a new project (local emulator)", value: "create" as const },
     ],
   });

As per coding guidelines: "Do NOT use as/any/type casts or anything else like that to bypass the type system."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stack-cli/src/commands/init.ts` around lines 60 - 66, Remove the
explicit ": string" annotation on the const mode declaration so TypeScript can
infer the union type from opts.mode and the select() choices; change "const
mode: string = opts.mode ?? await select(...)" to "const mode = opts.mode ??
await select(...)" (leave the select choices as "value: 'link' as const" etc.)
so subsequent branches that check mode (the if/else chain handling "create" |
"link" | "link-config" | "link-cloud") are exhaustively type-checked by the
compiler.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 60-66: Remove the explicit ": string" annotation on the const mode
declaration so TypeScript can infer the union type from opts.mode and the
select() choices; change "const mode: string = opts.mode ?? await select(...)"
to "const mode = opts.mode ?? await select(...)" (leave the select choices as
"value: 'link' as const" etc.) so subsequent branches that check mode (the
if/else chain handling "create" | "link" | "link-config" | "link-cloud") are
exhaustively type-checked by the compiler.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2958fdc2-8c31-401a-8bf9-3a2648b24abd

📥 Commits

Reviewing files that changed from the base of the PR and between 37e70ca and 233dd50.

📒 Files selected for processing (1)
  • packages/stack-cli/src/commands/init.ts

Adds the ability to create a project using cli.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
packages/stack-cli/src/commands/init.ts (2)

280-281: Avoid the non-null assertion; use ?? throwErr(...).

projects.find(...)! bypasses the type system. Although each branch above ensures projectId exists in projects, a future refactor could break that invariant silently. As per coding guidelines, "Code defensively. Prefer ?? throwErr(...) over non-null assertions, with good error messages explicitly stating the assumption that must've been violated".

♻️ Proposed fix
-  const project = projects.find((p) => p.id === projectId)!;
+  const project = projects.find((p) => p.id === projectId)
+    ?? throwErr(`Resolved projectId '${projectId}' is not present in the owned projects list`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stack-cli/src/commands/init.ts` around lines 280 - 281, Replace the
unsafe non-null assertion on the found project: instead of const project =
projects.find((p) => p.id === projectId)!;, use a null-check pattern with the
project's result combined with the project's error helper (e.g.,
projects.find(... ) ?? throwErr(...)) so that if the project is not found you
throw a descriptive error; update the code that passes project to
writeProjectKeysToEnv to use this defensively-checked value and include an
explicit message naming projectId and the failed assumption.

172-175: Import AdminOwnedProject from @stackframe/js instead of duplicating the type signature inline.

The inline type for the project parameter replicates the structure of AdminOwnedProject (both callers pass objects from user.createProject() and user.listOwnedProjects()). Inlining the createInternalApiKey method signature means SDK changes (new options, return fields) will silently diverge from this helper's type, reducing type safety.

♻️ Proposed refactor
-import { StackClientApp } from "@stackframe/js";
+import { StackClientApp, type AdminOwnedProject } from "@stackframe/js";
-async function writeProjectKeysToEnv(
-  project: { id: string, app: { createInternalApiKey: (opts: { description: string, expiresAt: Date, hasPublishableClientKey: boolean, hasSecretServerKey: boolean, hasSuperSecretAdminKey: boolean }) => Promise<{ publishableClientKey?: string | null, secretServerKey?: string | null }> } },
-  outputDir: string,
-) {
+async function writeProjectKeysToEnv(project: AdminOwnedProject, outputDir: string) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stack-cli/src/commands/init.ts` around lines 172 - 175, Replace the
inline project parameter type in writeProjectKeysToEnv with the exported
AdminOwnedProject type from `@stackframe/js`: import AdminOwnedProject and change
the function signature to accept project: AdminOwnedProject so it uses the SDK's
canonical shape (this ensures createInternalApiKey's options/return types stay
in sync with user.createProject() and user.listOwnedProjects()); update any
places that reference the inline type accordingly and run typecheck to catch any
required import/export adjustments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 62-78: runInit computes a resolved mode into the local variable
mode but handleLink still reads opts.mode, causing flags like
--config-file/--select-project-id to be ignored; fix by making handleLink use
the resolved mode: either pass mode as an explicit argument to handleLink (e.g.,
handleLink(opts, mode)) or set opts.mode = mode immediately before calling
handleLink so handleLink sees the inferred value; update handleLink's
signature/usage accordingly (and remove the now-dead "link" branch in any
switch/if that checks for mode === "link" since runInit never assigns that
value).
- Around line 184-189: The env assembly in envLines silently falls back to empty
strings for apiKey.publishableClientKey and apiKey.secretServerKey; instead
validate those fields after createInternalApiKey and throw if missing (e.g.,
using your throwErr helper or throw new Error) so you never write a broken .env.
Locate the code around createInternalApiKey and the envLines constant and assert
that apiKey.publishableClientKey and apiKey.secretServerKey are
non-null/undefined (throwing with a clear message mentioning
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY / STACK_SECRET_SERVER_KEY and
project.id) before interpolating them into envLines.

In `@packages/stack-cli/src/lib/create-project.ts`:
- Around line 27-35: The code unconditionally picks teams[0] after calling
user.listTeams() in create-project.ts, so users in multiple teams cannot choose
ownership; update the createProject flow to either accept a teamId on
CreateProjectOptions and pass it into user.createProject (propagate a --team-id
flag to CLI commands like init/project create), or when interactive and
teams.length > 1 prompt the user to select a team before calling
user.createProject; reference user.listTeams(), the teams array, and
user.createProject({ displayName, teamId }) to locate where to add the prompt or
hook up the new CreateProjectOptions.teamId parameter and use it instead of
teams[0].id.

---

Nitpick comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 280-281: Replace the unsafe non-null assertion on the found
project: instead of const project = projects.find((p) => p.id === projectId)!;,
use a null-check pattern with the project's result combined with the project's
error helper (e.g., projects.find(... ) ?? throwErr(...)) so that if the project
is not found you throw a descriptive error; update the code that passes project
to writeProjectKeysToEnv to use this defensively-checked value and include an
explicit message naming projectId and the failed assumption.
- Around line 172-175: Replace the inline project parameter type in
writeProjectKeysToEnv with the exported AdminOwnedProject type from
`@stackframe/js`: import AdminOwnedProject and change the function signature to
accept project: AdminOwnedProject so it uses the SDK's canonical shape (this
ensures createInternalApiKey's options/return types stay in sync with
user.createProject() and user.listOwnedProjects()); update any places that
reference the inline type accordingly and run typecheck to catch any required
import/export adjustments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5ac9f04c-93fb-4786-8c1c-8bdda2e99d3c

📥 Commits

Reviewing files that changed from the base of the PR and between 233dd50 and 5586a36.

📒 Files selected for processing (3)
  • packages/stack-cli/src/commands/init.ts
  • packages/stack-cli/src/commands/project.ts
  • packages/stack-cli/src/lib/create-project.ts

Comment thread packages/stack-cli/src/commands/init.ts
Comment thread packages/stack-cli/src/commands/init.ts
Comment thread packages/stack-cli/src/lib/create-project.ts
…PI keys

- handleLink now respects the mode resolved by runInit (from --config-file
  / --select-project-id), so those flags skip the interactive source prompt.
- writeProjectKeysToEnv throws via throwErr instead of silently writing
  empty publishable/secret keys to .env when the API returns null.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/stack-cli/src/commands/init.ts (1)

272-273: Use an explicit null check for autoCreatedProjectId.

autoCreatedProjectId is string | null, so else if (autoCreatedProjectId) works, but the repo convention is explicit nullishness checks for clarity.

♻️ Proposed fix
-  } else if (autoCreatedProjectId) {
+  } else if (autoCreatedProjectId != null) {
     projectId = autoCreatedProjectId;

As per coding guidelines: "Unless very clearly equivalent from types, prefer explicit null/undefinedness checks over boolean checks, eg. foo == null instead of !foo."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stack-cli/src/commands/init.ts` around lines 272 - 273, Replace the
truthy check on autoCreatedProjectId with an explicit null/undefined check:
change the condition that currently reads else if (autoCreatedProjectId) to else
if (autoCreatedProjectId != null) (or ==/!= variant per project style) so
projectId is assigned only when autoCreatedProjectId is not null/undefined;
update the branch around the projectId and autoCreatedProjectId variables
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 283-285: Replace the non-null assertion on the result of
projects.find with a defensive check that throws a clear error: change the
expression that assigns project (the one using projects.find((p) => p.id ===
projectId)!) to use the nullish-coalescing pattern with throwErr(...) instead
(e.g., projects.find(...) ?? throwErr(...)); include a concise message stating
the invariant that projectId must exist in projects; ensure this updated project
variable is then passed to writeProjectKeysToEnv unchanged.

---

Nitpick comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 272-273: Replace the truthy check on autoCreatedProjectId with an
explicit null/undefined check: change the condition that currently reads else if
(autoCreatedProjectId) to else if (autoCreatedProjectId != null) (or ==/!=
variant per project style) so projectId is assigned only when
autoCreatedProjectId is not null/undefined; update the branch around the
projectId and autoCreatedProjectId variables accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d521d7f9-c9e6-4020-a72c-6f7d86b716e5

📥 Commits

Reviewing files that changed from the base of the PR and between 5586a36 and ba2d75f.

📒 Files selected for processing (1)
  • packages/stack-cli/src/commands/init.ts

Comment thread packages/stack-cli/src/commands/init.ts
Comment thread packages/stack-cli/src/commands/init.ts Outdated
@nams1570 nams1570 requested review from mantrakp04 April 24, 2026 22:07
Comment thread packages/stack-cli/src/commands/init.ts
Comment thread packages/stack-cli/src/commands/init.ts
Comment thread packages/stack-cli/src/commands/init.ts Outdated
Comment thread packages/stack-cli/src/commands/init.ts Outdated
Comment thread packages/stack-cli/src/commands/init.ts
- Restore interactive "link existing" option alongside create
- Validate incompatible flag combos (e.g. --apps with --mode create-cloud)
- Fail upfront if --output-dir does not exist, avoiding orphan projects
- Prompt before overwriting an existing stack.config.ts
- Tighten mode union and convert dispatch to exhaustive switch
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/stack-cli/src/commands/init.ts (1)

38-42: ⚠️ Potential issue | 🟡 Minor

Non-interactive guard rejects --config-file / --select-project-id despite inference.

hasFlags only inspects opts.mode, but runInit infers the mode from opts.selectProjectId (→ link-cloud) and opts.configFile (→ link-config) at lines 97–100. As a result, a CI invocation like stack init --config-file ./stack.config.ts (or --select-project-id <id>) trips the guard and errors out with "stack init requires an interactive terminal. Use --mode flag for non-interactive usage." even though those flags fully determine the flow. Users are then forced to redundantly pass --mode link-config / --mode link-cloud for inference to ever take effect.

🐛 Proposed fix
-      const hasFlags = opts.mode != null;
+      const hasFlags = opts.mode != null || opts.selectProjectId != null || opts.configFile != null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stack-cli/src/commands/init.ts` around lines 38 - 42, The
non-interactive guard only checks hasFlags based on opts.mode, but runInit
infers mode from opts.selectProjectId and opts.configFile (see runInit and lines
where mode is derived), so update the guard to treat --select-project-id and
--config-file as non-interactive flags too; either expand hasFlags to (opts.mode
!= null || opts.selectProjectId != null || opts.configFile != null) or run the
same inference logic used in runInit to determine mode before enforcing
isNonInteractiveEnv() so CI calls like `stack init --config-file` /
`--select-project-id` are allowed without an explicit --mode.
♻️ Duplicate comments (1)
packages/stack-cli/src/commands/init.ts (1)

334-335: ⚠️ Potential issue | 🟡 Minor

Replace the ! non-null assertion with ?? throwErr(...).

projects.find(...) is typed as Project | undefined. By construction projectId is one of: from --select-project-id (validated against projects on line 317–320), the autoCreatedProjectId (just appended on line 311), or selected via the prompt — so the assumption holds, but the codebase convention is to encode it defensively rather than silence the type system.

🛡️ Proposed fix
-  const project = projects.find((p) => p.id === projectId)!;
+  const project = projects.find((p) => p.id === projectId)
+    ?? throwErr(`Selected project '${projectId}' unexpectedly missing from owned projects list`);
   await writeProjectKeysToEnv(project, outputDir);

As per coding guidelines: "Code defensively. Prefer ?? throwErr(...) over non-null assertions, with good error messages explicitly stating the assumption that must've been violated."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stack-cli/src/commands/init.ts` around lines 334 - 335, The code
uses a non-null assertion when resolving the selected project: replace "const
project = projects.find((p) => p.id === projectId)!" with a defensive check
using the helper throwErr so you don't silence the type system; i.e., call
projects.find((p) => p.id === projectId) and if it returns undefined invoke
throwErr with a clear message referencing projectId and the expectation (e.g.,
"selected projectId not found in projects"), then pass the resulting project to
writeProjectKeysToEnv; update any import or reference to throwErr if needed.
🧹 Nitpick comments (1)
packages/stack-cli/src/commands/init.ts (1)

57-57: Use explicit nullness checks for option flags (coding guideline).

Multiple sites in this file use truthy checks on string-valued options where the guideline calls for explicit != null/== null:

  • Line 57: if (opts.selectProjectId && opts.configFile)
  • Line 73: if (opts.mode)
  • Line 95: if (opts.mode), lines 97/99: else if (opts.selectProjectId) / (opts.configFile)
  • Line 316: if (opts.selectProjectId), line 322: else if (autoCreatedProjectId)

Aside from style, behavior subtly differs for empty strings (e.g., --apps "" on line 371): a truthy check treats "" as "not provided", whereas != null treats it as "provided but empty". The latter is what the guideline mandates and is also what makes validateOptions consistent — note line 75 already uses opts[key] != null, so the file is internally inconsistent.

As per coding guidelines: "Unless very clearly equivalent from types, prefer explicit null/undefinedness checks over boolean checks, eg. foo == null instead of !foo."

Also applies to: 73-73, 95-99, 316-322

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stack-cli/src/commands/init.ts` at line 57, Replace truthy checks
with explicit null/undefined checks for the option flags to preserve
empty-string semantics: change conditionals that read "if (opts.selectProjectId
&& opts.configFile)" and the other occurrences "if (opts.mode)" / "else if
(opts.selectProjectId) / (opts.configFile)" and "if (opts.selectProjectId)" /
"else if (autoCreatedProjectId)" so they use explicit != null (or == null where
appropriate) on the specific properties (opts.selectProjectId, opts.configFile,
opts.mode, autoCreatedProjectId) to detect presence versus absence; update all
referenced branches in the functions around those checks so behavior for empty
string values remains "provided but empty" and consistent with validateOptions
which already uses opts[key] != null.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 207-221: The try/catch in ensureLoggedInSession is too broad;
narrow the risky call to only resolveSessionAuth so we don't catch unrelated
errors. Call resolveSessionAuth inside a small try block, catch AuthError there,
handle non-interactive case via isNonInteractiveEnv -> throw CliError, otherwise
call performLogin(flags) and then re-run resolveSessionAuth to obtain
sessionAuth; finally return that sessionAuth and rethrow any non-AuthError
exceptions. Ensure you reference the resolveSessionAuth, AuthError,
performLogin, isNonInteractiveEnv, and CliError symbols when making the change.

---

Outside diff comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 38-42: The non-interactive guard only checks hasFlags based on
opts.mode, but runInit infers mode from opts.selectProjectId and opts.configFile
(see runInit and lines where mode is derived), so update the guard to treat
--select-project-id and --config-file as non-interactive flags too; either
expand hasFlags to (opts.mode != null || opts.selectProjectId != null ||
opts.configFile != null) or run the same inference logic used in runInit to
determine mode before enforcing isNonInteractiveEnv() so CI calls like `stack
init --config-file` / `--select-project-id` are allowed without an explicit
--mode.

---

Duplicate comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Around line 334-335: The code uses a non-null assertion when resolving the
selected project: replace "const project = projects.find((p) => p.id ===
projectId)!" with a defensive check using the helper throwErr so you don't
silence the type system; i.e., call projects.find((p) => p.id === projectId) and
if it returns undefined invoke throwErr with a clear message referencing
projectId and the expectation (e.g., "selected projectId not found in
projects"), then pass the resulting project to writeProjectKeysToEnv; update any
import or reference to throwErr if needed.

---

Nitpick comments:
In `@packages/stack-cli/src/commands/init.ts`:
- Line 57: Replace truthy checks with explicit null/undefined checks for the
option flags to preserve empty-string semantics: change conditionals that read
"if (opts.selectProjectId && opts.configFile)" and the other occurrences "if
(opts.mode)" / "else if (opts.selectProjectId) / (opts.configFile)" and "if
(opts.selectProjectId)" / "else if (autoCreatedProjectId)" so they use explicit
!= null (or == null where appropriate) on the specific properties
(opts.selectProjectId, opts.configFile, opts.mode, autoCreatedProjectId) to
detect presence versus absence; update all referenced branches in the functions
around those checks so behavior for empty string values remains "provided but
empty" and consistent with validateOptions which already uses opts[key] != null.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8ac51279-18dd-4474-bb98-87bfcfe97442

📥 Commits

Reviewing files that changed from the base of the PR and between ba2d75f and ff50c97.

📒 Files selected for processing (1)
  • packages/stack-cli/src/commands/init.ts

Comment thread packages/stack-cli/src/commands/init.ts
@BilalG1 BilalG1 merged commit 3b8667d into dev Apr 27, 2026
38 checks passed
@BilalG1 BilalG1 deleted the emulator-setup-fixes branch April 27, 2026 18:45
@promptless
Copy link
Copy Markdown
Contributor

promptless Bot commented Apr 27, 2026

Promptless prepared a documentation update related to this change.

Triggered by PR #1379

Added comprehensive CLI init options reference to the setup guide, documenting the new create-cloud mode, restored interactive prompts for project creation and linking, and improved non-interactive mode handling.

Review: Document CLI init options

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants